package promise

import (
	"context"
	"errors"
	"gitee.com/shijun123456/kcgin/logs"
	"sync"
	"time"
)

type Resolve func(interface{})
type Reject func(interface{})
type PromiseFunc func(resolve Resolve, reject Reject)
type PromiseAttrFunc func(*Promise)
type PromiseAttrFuncs []PromiseAttrFunc

func (this PromiseAttrFuncs) apply(promise *Promise) {
	for _, f := range this {
		f(promise)
	}
}
func WithTimeOut(t time.Duration) PromiseAttrFunc {
	return func(promise *Promise) {
		promise.timeOut = t
	}
}

var (
	RejectError  = errors.New("rectError")
	TimeOutError = errors.New("timeoutError")
)

type Promise struct {
	resolve     Resolve
	reject      Reject
	wg          *sync.WaitGroup
	timeOut     time.Duration
	promiseFunc PromiseFunc
}

func New(fn PromiseFunc, attr ...PromiseAttrFunc) *Promise {
	p := new(Promise)
	p.promiseFunc = fn
	p.wg = &sync.WaitGroup{}
	PromiseAttrFuncs(attr).apply(p)
	return p
}
func (p *Promise) Then(resolve Resolve) *Promise {
	p.resolve = resolve
	return p
}
func (p *Promise) Catch(reject Reject) *Promise {
	p.reject = reject
	return p
}
func (p *Promise) Done() *Promise {
	defer func() {
		if err := recover(); err != nil {
			if e, ok := err.(error); ok {
				p.reject(e)
				if errors.Is(e, TimeOutError) {
					logs.Info("timeoutErr: %s", e.Error())
				}
				if errors.Is(e, RejectError) {
					logs.Info("normalPanicerr: %s", e.Error())
				}
			}

		}
	}()
	p.wg.Add(1)
	go func() {
		defer p.wg.Done()
		p.promiseFunc(p.resolve, p.reject)
	}()
	p.wg.Wait()
	ctx, _ := context.WithTimeout(context.Background(), p.timeOut)
	select {
	case <-ctx.Done():
		panic(TimeOutError)
	}
	return p
}
